/**
 * Copyright 2006-2008 Michele Mostarda ( michele.mostarda@gmail.com ).
 * All Rights Reserved.
 * <p/>
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.graphnav.demo.model;

import com.hp.hpl.jena.db.DBConnection;
import com.hp.hpl.jena.db.IDBConnection;
import com.hp.hpl.jena.db.ModelRDB;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.util.FileManager;
import org.graphnav.swing.DefaultArc;
import org.graphnav.swing.DefaultNode;
import org.graphnav.swing.Node;
import org.graphnav.model.DefaultGraphModel;

import java.awt.Point;
import java.io.InputStream;
import java.util.List;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;

/**
 * Extends the <code>DefaultGraphModel</code> to wrap
 * an RDF model provided by the Jena framework.
 * (see <a href="http://jena.sourceforge.net/">Jena Framework</a>)
 */
public class JenaGraphModel extends DefaultGraphModel {

    /* Some constants. */

    static final String M_DB_URL          = "jdbc:oracle:thin:@<HOST>:1521:<database>";
    static final String M_DB_USER         = "<user>";
    static final String M_DB_PASSWD       = "<password>";
    static final String M_MODEL_NAME      = "<model-name>";
    static final String M_DB              = "Oracle";
    static final String M_DBDRIVER_CLASS  = "oracle.jdbc.OracleDriver";

    /**
     * Wraps a Jena node with a Default Node.
     */
    protected class JenaNode extends DefaultNode {

        /**
         * The represented RDF node.
         */
        private RDFNode rdfNode;

        /**
         * Contructor.
         *
         * @param rdfNode
         * @param location
         */
        public JenaNode(RDFNode rdfNode, Point location) {
            super(rdfNode.toString(), location);
            this.rdfNode = rdfNode;
        }

        /**
         * Returns the RDF node.
         *
         * @return
         */
        public RDFNode getRdfNode() {
            return rdfNode;
        }

        public String getDescription() {
            return rdfNode.toString();
        }

        public boolean equals(Object obj) {
            if(obj == null) {
                return false;
            }
            if(obj == JenaNode.this) {
                return true;
            }
            if(obj instanceof JenaNode) {
                JenaNode other = (JenaNode) obj;
                return rdfNode.equals(other.rdfNode);
            } else {
                return false;
            }
        }

        public int hashCode() {
            return rdfNode.hashCode();
        }
    }

    /**
     * Wraps a Jena arc with a Default Arc.
     */
    protected class JenaArc extends DefaultArc {

        /**
         * The wrapped property.
         */
        private Property property;

        /**
         * Constructor.
         * @param property
         * @param from
         * @param to
         */
        public JenaArc(Property property, Node from, Node to) {
            super(from, to);
            this.property = property;
        }

        public String getDescription() {
            return property.getURI();
        }
    }

    /**
     * The internal Jena model.
     */
    private Model jenaModel;

    /**
     * Creates a <code>JenaGraphModel</code> from a RDF file.
     *
     * @param filename
     * @return
     */
    public static JenaGraphModel createFromFile(String filename) {
        Model model = ModelFactory.createDefaultModel();
        InputStream in = FileManager.get().open( filename );
        if (in == null) {
            throw new IllegalArgumentException(
                                 "File: " + filename + " not found");
        }
        model.read(in, "");
        return new JenaGraphModel( model );
    }

    /**
     * Creates a <code>JenaGraphModel</code> from a connection URL.
     *
     * @return
     * @throws ClassNotFoundException
     */
    public static JenaGraphModel createFromURL() throws ClassNotFoundException {
        Class.forName(M_DBDRIVER_CLASS);
        System.out.print("Retrieving Jena Model...");
        IDBConnection conn = new DBConnection(M_DB_URL, M_DB_USER, M_DB_PASSWD, M_DB);
        ModelRDB rdbModel = ModelRDB.open(conn, M_MODEL_NAME);
        System.out.println(" done");
        return new JenaGraphModel( rdbModel );
    }

    /**
     * Creates a <code>JenaGraphModel</code> from a Jena model.
     *
     * @param jenaModel
     */
    public JenaGraphModel(Model jenaModel) {
        this.jenaModel = jenaModel;
        Map<RDFNode,Node> uniqueNodes = new HashMap<RDFNode,Node>();
        StmtIterator iterator = jenaModel.listStatements();
        List statementsList = iterator.toList();
        Statement statement;
        Node from, to;
        RDFNode subject, object;
        for(Iterator statements = statementsList.iterator(); statements.hasNext(); ) {
            statement = (Statement) statements.next();
            subject = statement.getSubject();
            object  = statement.getObject();
            from = (Node) uniqueNodes.get(subject);
            if(from == null) {
                from = new JenaNode(subject, nextLocation());
                uniqueNodes.put(subject, from);
            }
            to = (Node) uniqueNodes.get(object);
            if(to == null) {
                to = new JenaNode(object, nextLocation());
                uniqueNodes.put(object, to);
            }
            arcs.add(new JenaArc(statement.getPredicate(), from, to));
        }
        iterator.close();
        nodes.addAll(uniqueNodes.values());
        uniqueNodes.clear();
    }

    /**
     * The canvas width.
     */
    static final int canvasWidth  = 400;

    /**
     * The canvas height.
     */
    static final int canvasHeight = 300;

    protected Point nextLocation() {
        Random rand = new Random();
        return new Point(rand.nextInt(canvasWidth), rand.nextInt(canvasHeight));
    }

    public void startVisit() {
        //EMPTY.
    }

    public void endVisit() {
        //EMPTY.
    }

}
